package wechat

import (
	"JK-Junior-Go-Engineer-Camp/webook/internal/domain"
	"JK-Junior-Go-Engineer-Camp/webook/pkg/logger"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

type Service interface {
	// AuthURL 拼接微信登录地址
	AuthURL(ctx context.Context, state string) (string, error)
	// VerifyCode 通过code获取access_token
	VerifyCode(ctx context.Context, code string) (domain.WechatInfo, error)
}

// 对回调地址进行转义 todo: 这个回调地址记得修改
var redirectURL = url.PathEscape("https://hyydsb.online/oauth2/wechat/callback")

type service struct {
	appID     string
	appSecret string
	client    *http.Client
	l         logger.LoggerV1
}

func NewService(appID string, appSecret string, l logger.LoggerV1) Service {
	return &service{
		appID:     appID,
		appSecret: appSecret,
		client:    http.DefaultClient,
	}
}

// AuthURL 拼接微信登录地址
func (s *service) AuthURL(ctx context.Context, state string) (string, error) {
	const authURLPattern = `https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect`
	return fmt.Sprintf(authURLPattern, s.appID, redirectURL, state), nil
}

// VerifyCode 通过code获取access_token
func (s *service) VerifyCode(ctx context.Context, code string) (domain.WechatInfo, error) {
	const accessTokenPattern = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code`
	req, err := http.NewRequestWithContext(ctx, http.MethodGet,
		fmt.Sprintf(accessTokenPattern, s.appID, s.appSecret, code), nil,
	)
	if err != nil {
		return domain.WechatInfo{}, err
	}
	resp, err := s.client.Do(req)
	if err != nil {
		return domain.WechatInfo{}, err
	}
	var res Result
	err = json.NewDecoder(resp.Body).Decode(&res)
	if err != nil {
		return domain.WechatInfo{}, err
	}
	if res.ErrCode != 0 { // 请求失败了
		return domain.WechatInfo{},
			fmt.Errorf("调用微信接口失败, ErrCode:%d, ErrMsg:%s",
				res.ErrCode, res.ErrMsg)
	}
	return domain.WechatInfo{
		UnionId: res.UnionId,
		Openid:  res.Openid,
	}, nil
}

// Result 微信登录通过code获取access_token一步中，微信返回的结构
type Result struct {
	// 正确响应字段
	// 接口调用凭证
	AccessToken string `json:"access_token"`
	// access_token接口调用凭证超时时间，单位（秒）
	ExpiresIn int `json:"expires_in"`
	// 用户刷新access_token
	RefreshToken string `json:"refresh_token"`
	// 授权用户唯一标识
	Openid string `json:"openid"`
	// 用户授权的作用域，使用逗号（,）分隔
	Scope string `json:"scope"`
	// 当且仅当该网站应用已获得该用户的userinfo授权时，才会出现该字段。
	UnionId string `json:"unionid"`

	// 错误响应字段
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}
